package tt.dz.order.vo;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import tt.dz.order.exception.BusinessException;
import tt.dz.order.exception.ParamLackException;
import tt.dz.order.exception.ParamNotFoundInSystemException;
import tt.dz.order.pojo.OrderAccountingStatus;
import tt.dz.order.pojo.OrderInfo;
import tt.dz.order.pojo.OrderItem;
import tt.dz.order.pojo.OrderServiceInfo;
import tt.dz.order.service.drools.ActivityRuleService;
import tt.dz.order.util.OrderConstant;
import tt.dz.order.util.OrderConstant.AdvancePaymentStage;
import tt.dz.order.util.OrderConstant.OrderPayChannelCode;
import tt.dz.order.util.OrderConstant.OrderPayWay;
import tt.dz.order.util.OrderConstant.OrderStatus;
import tt.dz.order.util.ThreadSafeDateFormat;
import tt.dz.order.util.UUIDUtil;;

/**
 * 预付款订单创建VO
 * 
 * @name
 * @author Anson
 * @version 1.0
 * @summary
 * @desc
 *
 */
public class AdvanceOrder extends BaseVo {

	private static final BigDecimal ZERO = new BigDecimal(0);

	// 用户id
	private transient String userId;
	// 用户类型，0 个人用户，1 企业账户,2 支持统付的个人客户
	private transient String userType;
	// (必须) 卡号
	private String cardNo;
	// (必须) 卡类型 08 虚拟，06 实体卡  ，07  身份识别卡
	private String cardType;
	// (必须) 用户所属运营商编码
	private transient String merchantCode;
	// (非必须) 卡上的余额
	private String payerBalance;
	// (必须) 来源系统 ,用户使用什么app下的单：104 自主app, 201乐视
	private String appId;
	// (必须) 支付阶段:(1, "预付款"),(2, "收入"),(3, "差额平衡")
	private int paymentStage = -1;
	// (非必须)主服务编号
	private String mainServiceNo;
	// (非必须)主服务名称
	private String mainServiceName;
	/**
	 * 关联订单号<br>
	 * 关联订单号:预付款在首次的时候不会订单，故在paymentStage=1的时候，可以不传<br>
	 * 若其他订单阶段的时候，是必须传入订单号，否则会抛出异常
	 */
	private String relatedOrderNo;
	// (必须) 订单项集合
	private List<AdvanceOrderItem> orderItems;

	/**
	 * 订单校验
	 * 
	 * @throws ParamLackException
	 * @throws ParamNotFoundInSystemException
	 */
	public void validate() throws ParamLackException, ParamNotFoundInSystemException {
		if (StringUtils.isBlank(cardType)) {
			throw new ParamLackException("cardType为空");
		}
		if (StringUtils.isBlank(cardNo)) {
			throw new ParamLackException("cardNo为空");
		}
		if (StringUtils.isBlank(appId)) {
			throw new ParamLackException("appId为空");
		}
		if (paymentStage == -1) {
			throw new ParamLackException("paymentStage为空");
		}
		if (orderItems == null || orderItems.isEmpty()) {
			throw new ParamLackException("orderItems为空");
		}
		for (AdvanceOrderItem advanceOrderItem : orderItems) {
			advanceOrderItem.validate();
		}
		// 关联订单号校验
		this.validateRelateNo();
	}

	/**
	 * 校验关联订单号<br>
	 * 若是处于paymentStage=1的时候，会自身关联自身,并且关联订单号为空<br>
	 * 其他情况，若不存在关联订单号，则会抛出异常
	 * 
	 * @throws ParamLackException
	 * @throws ParamNotFoundInSystemException
	 */
	private void validateRelateNo() throws ParamLackException, ParamNotFoundInSystemException {
		boolean isAdvance = OrderConstant.AdvancePaymentStage.isAdvance(paymentStage);
		if (!isAdvance && StringUtils.isEmpty(this.relatedOrderNo)) {
			throw new ParamLackException(
					"并非处于" + OrderConstant.AdvancePaymentStage.ADVANCE.desc() + "阶段，relatedOrderNo为空");
		}
		if (!isAdvance && OrderInfo.getById(relatedOrderNo) == null) {
			throw new ParamLackException("预付款中未能通过关联订单查询到订单信息");
		}
	}

	/**
	 * 处理订单主服务编号
	 */
	public void dealMainServiceNo() throws ParamLackException, ParamNotFoundInSystemException {
		if (StringUtils.isEmpty(this.mainServiceNo) && this.orderItems != null) {
			AdvanceOrderItem item = this.orderItems.get(0);
			String itemServiceNo = item.getServiceNo();
			OrderServiceInfo serviceInfo = OrderServiceInfo.getById(itemServiceNo);
			if (serviceInfo == null) {
				throw new ParamLackException("预付款中处理主服务编号中获取子服务的信息为空");
			}
			this.mainServiceNo = serviceInfo.getParentNo();
		}
		OrderServiceInfo mainServiceInfo = OrderServiceInfo.getById(this.mainServiceNo);
		if (mainServiceInfo == null) {
			throw new ParamLackException("预付款中，子服务和主服务关联关系未找到");
		}
		this.mainServiceName = mainServiceInfo.getName();
	}

	/**
	 * 订单号创建
	 * 
	 * @return
	 * @throws BusinessException
	 */
	public String generalOrderNo() throws BusinessException {

		if (StringUtils.isEmpty(this.userType)) {
			throw new BusinessException("预付款订单号创建中，依赖的用户类型未找到");
		}
		if (StringUtils.isEmpty(this.appId)) {
			throw new BusinessException("预付款订单号创建中，依赖的appid未找到");
		}
		StringBuilder sb = new StringBuilder(this.getUserType()).append(this.appId)
				.append(UUIDUtil.getCurrentDate14(null)).append(UUIDUtil.randomNum(4));
		return sb.toString();
	}

	/**
	 * 处理当前关联订单号<br>
	 * 执行这个应该在能够回去到当前订单号之后去执行
	 * 
	 * @param curOrderNo
	 *            当前订单的订单编号
	 * @return
	 * @throws BusinessException
	 */
	public String generalRelateNo(String curOrderNo) throws BusinessException, ParamLackException {
		if (StringUtils.isEmpty(curOrderNo)) {
			throw new ParamLackException("预付款关联订单号处理，依赖的当前的订单号未找到");
		}
		/**
		 * 当前并非处于预付款阶段
		 */
		if (!OrderConstant.AdvancePaymentStage.isAdvance(paymentStage)) {
			if (StringUtils.isEmpty(this.relatedOrderNo)) {
				// 当前并非处于预付款阶段，但关联订单为空
				throw new ParamLackException(
						"并非处于" + OrderConstant.AdvancePaymentStage.ADVANCE.desc() + "阶段，relatedOrderNo为空");

			}
			return this.relatedOrderNo;
		}
		this.relatedOrderNo = curOrderNo;
		return this.relatedOrderNo;
	}

	/**
	 * 相关熟悉拷贝到OrderBaseData中
	 * 
	 * @param orderBaseData
	 */
	public void copyPropertiesToOrderBaseData(OrderBaseData orderBaseData) {
		// 用户id
		orderBaseData.setUserId(this.getUserId());
		// 用户类型，0 个人用户，1 企业账户,2 支持统付的个人客户
		orderBaseData.setUserType(this.getUserType());
		// (必须) 卡号
		orderBaseData.setCardNo(this.getCardNo());
		// (必须) 卡类型 08 虚拟，06 实体卡  ，07  身份识别卡
		orderBaseData.setCardType(this.getCardType());
		// (必须) 用户所属运营商编码
		orderBaseData.setMerchantCode(this.getMerchantCode());
		// (必须) 卡上的余额 ，如果卡类型是"06 线下卡",需要OMS在第1次调用时传递卡上的余额
		orderBaseData.setPayerBalance(this.getPayerBalance());
		// (必须) 来源系统 ,用户使用什么app下的单：104 自主app, 201乐视
		orderBaseData.setAppId(this.getAppId());
	}

	/**
	 * 获取当前支付阶段
	 * 
	 * @return
	 */
	public AdvancePaymentStage getAdvancePaymentStage() {
		AdvancePaymentStage stage = OrderConstant.AdvancePaymentStage.valueOfByInt(this.paymentStage);
		return stage;
	}

	/**
	 * 预付款根据不同的状态，计算项不一样的业务逻辑<br>
	 * 若是预付款阶段，则不会进行相关计算，直接创建成orderItem<br>
	 * 若是收费阶段，则会计算其打折计算，则会调用drool引擎计算优惠<br>
	 * 
	 * @param orderBaseDatas
	 * @param activityRuleService
	 * @return
	 */
	public List<OrderItem> caculate(List<OrderBaseData> orderBaseDatas, ActivityRuleService activityRuleService)
			throws BusinessException {
		AdvancePaymentStage stage = this.getAdvancePaymentStage();
		if (stage == null) {
			throw new BusinessException("预付款根据不同的状态，计算项不一样的业务逻辑中当前阶段值，不存在对应的阶段[stage=" + this.paymentStage + "]");
		}
		List<OrderItem> orderItems = null;
		OrderItem item = null;
		switch (stage) {
		case ADVANCE:
			orderItems = new ArrayList<OrderItem>();
			for (OrderBaseData orderBaseData : orderBaseDatas) {
				item = new OrderItem(orderBaseData);
				item.setOrderBaseData(orderBaseData);
			}
			orderItems.add(item);
			break;

		case INCOME:
			// 若是收入类，则会计算其打折计算
			orderItems = activityRuleService.caculate(orderBaseDatas);
			break;

		case BALANCE_ALLOWANCE:
			orderItems = new ArrayList<OrderItem>();
			for (OrderBaseData orderBaseData : orderBaseDatas) {
				item = new OrderItem(orderBaseData);
				item.setOrderBaseData(orderBaseData);
			}
			orderItems.add(item);
			break;

		default:

			break;
		}
		if (orderItems == null) {
			throw new BusinessException("当前阶段值，不存在对应的阶段[stage=" + this.paymentStage + "]");
		}
		return orderItems;
	}

	/**
	 * 后置服务，根据不同的预付款不同阶段，进行相关联状态和退款相关的后续操作<br>
	 * A.若为第一阶段预付款阶段的时候，是根据回调接口确定下来，确定了其支付方式以及状态<br>
	 * B.差额平衡的时候，计算当前所有关联的订单整体的差额计算<br>
	 * --1根据当前关联订单下支付方式添加当前的支付方式 --2若当前订单项为正则表示，当前需要进行重新充值，<br>
	 * --3若为负值则表示需要退款,并发送台帐,收入阶段与平衡差额阶段的状态都是同步的<br>
	 * C.收入阶段，支付方式是通过预付款形式，支付未完成<br>
	 * 
	 * @param orderInfo
	 * @param orderList
	 * @return
	 */
	public OrderInfo postService(OrderInfo orderInfo, List<OrderItem> orderList) throws BusinessException {
		// 当前订单阶段
		AdvancePaymentStage advancePaymentStage = this.getAdvancePaymentStage();
		// 付款方
		orderInfo.setRealPayer(this.cardNo);
		// 当前真实价格
		BigDecimal orderBalanceAllowance = orderInfo.getCaculatedTotalPrice();
		switch (advancePaymentStage) {

		case ADVANCE:
			// 第一个阶段，预付款阶段
			// 支付状态有支付宝回调决定
			orderInfo.save();
			break;

		case INCOME:
			// 收入阶段
			// 支付方式为预付款,并且是未完成
			orderInfo.setStatus(OrderStatus.unPay.value());
			orderInfo.setPayTypeCode(OrderPayWay.IMPREST_PAY.value());
			orderInfo.save();
			break;

		case BALANCE_ALLOWANCE:
			// 差额平衡阶段
			String relateOrderNo = orderInfo.getRelatedOrderNo();
			if (StringUtils.isEmpty(relateOrderNo)) {
				throw new BusinessException("预付款中差额平衡阶段的所关联的订单号为空[orderNo=" + orderInfo.getOrderNo() + "]");
			}
			// 若当前真实价格位负数，则表示需要退款
			// 若是正数，表示需要不差价
			int sub = orderBalanceAllowance.compareTo(ZERO);
			if (sub !=0) {
				// 当前为负数，则表示需要退款
				// 则有业务系统发起退款状态
				// 大于0 ， 则需要充值
				// 支付状态和台帐发送由回调接口决定
				orderInfo.save();
			}else {
				// 刚好相等，则表示不需要退款以及不需要充值不差价
				// 支付方式为预付款阶段的支付方式,并且是完成
				// 关联的订单号为已支付
				// 关联订单状态已支付，并且发送当前台帐
				// 预付款阶段的支付方式为“第三方实时支付”的时候，当前刚好平等，则不会发生额外的交易记录，
				// 故不会第三方交易记录
				// 获取收入阶段的订单
				OrderInfo incomeOrder = OrderInfo.getOrderByRelateNoAndStage(relateOrderNo,
						AdvancePaymentStage.INCOME.value());
				// 预收阶段的订单
				OrderInfo advanceOrder = OrderInfo.getOrderByRelateNoAndStage(relateOrderNo,
						AdvancePaymentStage.ADVANCE.value());
				if (incomeOrder == null || advanceOrder == null) {
					throw new BusinessException("未找到预付款中差额平衡阶段的所关联的订单号[orderNo=" + orderInfo.getOrderNo() + "]");
				}
				// 预付款阶段支付方式
				int advancePayWay = advanceOrder.getPayTypeCode();
				OrderPayChannelCode payChannelCode = OrderPayChannelCode.get(advanceOrder.getPayChannelCode());
				orderInfo.setPayTypeCode(advancePayWay);
				if (payChannelCode != null) {
					orderInfo.setPayChannelCode(payChannelCode.value());
				}
				this.saveBalanceOrderPayDoneServiceNoThdTradeRecord(orderInfo, incomeOrder, advancePayWay);
			}

			break;

		default:
			break;
		}

		return orderInfo;
	}

	/**
	 * 持久化已完成的订单状态<br>
	 * 若是第三方实时支付的时候，当前方式不会真实的产生交易记录，将会采取模拟形式<br>
	 * 记录号直接赋值为{@link OrderConstant#NULL_THIRDIMMEDIATELY_TRADE_NO}<br>
	 * 交易时间点为当前时间<br>
	 * 当前交易金额为订单类型交易金额<br>
	 * 记录流水状态直接赋值为{@link OrderConstant#NULL_THIRDIMMEDIATELY_TRADE_STATUS}
	 * 
	 * @param orderInfo
	 *            当前订单号“差额平衡”阶段
	 * @param incomeOrder
	 *            收入阶段订单
	 * @param advancePayWay
	 *            预支付阶段订单支付方式
	 */
	private void saveBalanceOrderPayDoneServiceNoThdTradeRecord(OrderInfo orderInfo, OrderInfo incomeOrder,
			int advancePayWay) {
		OrderPayWay payWay = OrderPayWay.get(advancePayWay);
		if (OrderPayWay.thirdImmediately == payWay) {
			orderInfo.setThdEndTime(ThreadSafeDateFormat.format(new Date(), ThreadSafeDateFormat.TIME_FMT));
			orderInfo.setThdPayAmount(orderInfo.getCaculatedTotalPrice());
			orderInfo.setThdPaySeq(OrderConstant.NULL_THIRDIMMEDIATELY_TRADE_NO);
			orderInfo.setTradeStatusCode(OrderConstant.NULL_THIRDIMMEDIATELY_TRADE_STATUS);
		}
		orderInfo.save();
		// 当前收入阶段订单状态更新为已支付，并且发送台帐
		this.updateOrderPayDoneAndSendAccount(incomeOrder);
		// 当前订单状态更新为已支付，并且发送台帐
		this.updateOrderPayDoneAndSendAccount(orderInfo);
	}

	/**
	 * 更新当前订单状态并且建立台帐
	 * 
	 * @param order
	 * @return
	 */
	private OrderInfo updateOrderPayDoneAndSendAccount(OrderInfo order) {
		order.setStatus(OrderStatus.payedDone.value());
		order.update();
		this.createAccuntStatus(order.getOrderNo());
		return order;
	}

	/**
	 * 创建订单状态
	 * 
	 * @param orderNo
	 * @return
	 */
	public OrderAccountingStatus createAccuntStatus(String orderNo) {
		OrderAccountingStatus oas = new OrderAccountingStatus();
		oas.setOrderNo(orderNo);
		oas.save();
		return oas;
	}

	public String getUserId() {
		return userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}

	public String getUserType() {
		return userType;
	}

	public void setUserType(String userType) {
		this.userType = userType;
	}

	public String getCardNo() {
		return cardNo;
	}

	public void setCardNo(String cardNo) {
		this.cardNo = cardNo;
	}

	public String getCardType() {
		return cardType;
	}

	public void setCardType(String cardType) {
		this.cardType = cardType;
	}

	public String getMerchantCode() {
		return merchantCode;
	}

	public void setMerchantCode(String merchantCode) {
		this.merchantCode = merchantCode;
	}

	public String getPayerBalance() {
		return payerBalance;
	}

	public void setPayerBalance(String payerBalance) {
		this.payerBalance = payerBalance;
	}

	public String getAppId() {
		return appId;
	}

	public void setAppId(String appId) {
		this.appId = appId;
	}

	public String getMainServiceNo() {
		return mainServiceNo;
	}

	public void setMainServiceNo(String mainServiceNo) {
		this.mainServiceNo = mainServiceNo;
	}

	public int getPaymentStage() {
		return paymentStage;
	}

	public void setPaymentStage(int paymentStage) {
		this.paymentStage = paymentStage;
	}

	public String getMainServiceName() {
		return mainServiceName;
	}

	public void setMainServiceName(String mainServiceName) {
		this.mainServiceName = mainServiceName;
	}

	public String getRelatedOrderNo() {
		return relatedOrderNo;
	}

	public void setRelatedOrderNo(String relatedOrderNo) {
		this.relatedOrderNo = relatedOrderNo;
	}

	public List<AdvanceOrderItem> getOrderItems() {
		return orderItems;
	}

	public void setOrderItems(List<AdvanceOrderItem> orderItems) {
		this.orderItems = orderItems;
	}

}
